using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;

namespace MediaOwl.Cryptos.Services
{
    [Export(typeof(XorChiffre))]
    public class XorChiffre
    {
        public string Encrypt(string text, int key)
        {
            var inSb = new StringBuilder(text);
            var outSb = new StringBuilder(text.Length);
            for (int i = 0; i < text.Length; i++)
            {
                char c = inSb[i];
                c = (char)(c ^ key);
                outSb.Append(c);
            }
            return outSb.ToString();
        }

        public string Decrypt(string text, int key)
        {
            return Encrypt(text, key);
        }

        public string EncryptOneTimePad(string text)
        {
            string oneTimePad = GetOneTimePad(text.Length);

            return text.Select((c, i) => (char) (c ^ oneTimePad[i])).Aggregate(string.Empty, (current, c) => current + c);
        }

        public string DecryptOneTimePad(string text)
        {
            return EncryptOneTimePad(text);
        }

        private static string GetOneTimePad(int length)
        {
            string primePad = string.Empty;
            const int upperBound = 500; // Zahl, bis zu der gesucht werden soll, eingeben
            var tagged = new bool[upperBound + 1]; // Array zur Speicherung, ob die zum Index passende Zahl gestrichen wurde
            int i;
            // Array mit false initialisieren
            for (i = 0; i < tagged.Length; i++)
                tagged[i] = false;

            // alle Zahlen bis zur Wurzel der oberen Zahl durchlaufen
            for (i = 2; i < Math.Ceiling(Math.Sqrt(upperBound)); i++)
            {
                if (!tagged[i])
                {
                    int j = i;
                    // alle Vielfachen der Zahl i, angefangen von ihrer Quadratzahl, durchstreichen
                    while (j * i <= upperBound)
                    {
                        tagged[j * i] = true;
                        j++;
                    }
                }
            }

            // alle nicht durchgestrichenen Zahlen ausgeben
            for (i = 2; i < tagged.Length; i++)
            {
                if (!tagged[i])
                    primePad += i.ToString();
            }

            string otp = string.Empty;
            i = 0;
            while (otp.Length < length)
            {
                otp += primePad[i];
                i++;
                if (i == primePad.Length)
                {
                    i = 0;
                }
            }

            return otp;
        }
    }
}